package nba2kmodtool.filedesc;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import nba2kmodtool.util.ByteFileReader;
import nba2kmodtool.util.PluginDDS;
import nba2kmodtool.util.PluginDDS.DDSHEADER;

/**
 * NBA2K Game Texture File Reader
 * @company ZSword (C) Copyright
 * @author JemiZhuu(周士淳)
 * @category N2kTextureReader
 * @version 1.0
 * @since 2011-11-16
 */
public class N2kTextureReader {
	private static final boolean IS_DEBUG = BaseIffFile.IS_DEBUG;

	protected BaseIffFile iff;
	protected long fileType;
	protected File file;
	protected File cdfFile;

	protected N2kTextureReader(BaseIffFile iff) {
		this.iff = iff;
		this.fileType = iff.fileType;
		this.file = iff.file;
		this.cdfFile = iff.cdfFile;
	}

	/**
	 * Read Texture File
	 * @param index
	 * @return
	 * @throws IOException
	 */
	public N2kTextureFile readTextureFile(int index) throws IOException {
		N2kTextureFile dataFile = new N2kTextureFile();
		ByteFileReader in = null;
		IffDataFile rawFile = iff.dataFiles[index];
		if (fileType == 0xFF3BEF94L) {
			in = new ByteFileReader(file, "r");
		} else if (fileType == 0xF0985030L) {
			in = new ByteFileReader(cdfFile, "r");
		}
		in.seek(rawFile.partAddr[0]);
		dataFile.headerStart = in.getFilePointer();
		dataFile.serial = in.readLong();
		byte[] buf = new byte[4];
		in.read(buf);
		dataFile.pixelFormat = new String(buf);
		if (IS_DEBUG) {
			System.out.printf("head:%X-", dataFile.headerStart);
		}
		if (buf[0] != 'D' || buf[1] != 'X' || buf[2] != 'T') {
			if (IS_DEBUG) {
				System.out.printf("BMP: %X-%X-%X-%X\n", buf[0], buf[1], buf[2], buf[3]);
			}
			dataFile.bmpFlag = buf[0];
			dataFile.pixelFormat = "BMP";
			dataFile.fileType = "bmp";
		} else {
			dataFile.fileType = "dds";
		}
		in.skipBytes(0x5B);
		dataFile.flag = (short) in.readBytesAsInt(1);
		dataFile.width = in.readBytesAsInt(2);
		dataFile.height = in.readBytesAsInt(2);
		if (IS_DEBUG) {
			System.out.printf("Size: %d-%d\n", dataFile.width, dataFile.height);
		}
		/*if ("BMP".equals(dataFile.pixelFormat)) {
			if (dataFile.bmpFlag == 0x29) {
				dataFile.width = 1024;
				dataFile.height = 128;
			} else if (dataFile.bmpFlag == 0x32) {
				dataFile.width = 512;
				dataFile.height = 16;
			} else if (dataFile.bmpFlag == 0x51) {
				dataFile.width = 512;
				dataFile.height = 256;
			}
		}*/
		in.skipBytes(8);
		long dataBaseAddr = iff.dataFiles[index].partAddr[0] + 0x74;
		if (iff.dataFiles[index].partNum > 1) {
			dataBaseAddr = iff.dataFiles[index].partAddr[1];
		}
		if (rawFile.indexType > 0) {
			dataFile.indexType = rawFile.indexType;
			dataFile.index = rawFile.index;
		} else {
			dataFile.dataStart = dataBaseAddr + in.read4Bytes() - 1;
		}
		long size = (dataFile.width * dataFile.height);
		long minSize = 16;
		if ("DXT1".equals(dataFile.pixelFormat)) {
			size /= 2;
			minSize /= 2;
		}
		int mip = 8;
		if (dataFile.flag == 0x05)
			mip = 10;
		else if (dataFile.flag == 0x03)
			mip = 7;
		else if (dataFile.flag == 0x01)
			mip = 3;
		dataFile.mip = mip;
		long mipSize = size;
		for (int s = 2; s <= mip; s++) {
			mipSize /= 4;
			mipSize = (mipSize < minSize) ? minSize : mipSize;
			size += mipSize;
		}
		dataFile.dataSize = size;
		in.close();
		return dataFile;
	}

	/**
	 * Read Texture data
	 * @param src
	 * @return
	 * @throws IOException
	 */
	public BufferedImage readN2KTexture(N2kTextureFile src) throws IOException {
		if ("BMP".equals(src.getPixelFormat())) {
			return this.getBMPTexture(src);
		} else {
			return this.getDDSTexture(src);
		}
	}

	/**
	 * Get BPM Texture data
	 * @param src
	 * @return
	 * @throws IOException
	 */
	private BufferedImage getBMPTexture(N2kTextureFile src) throws IOException {
		ByteFileReader io = null;
		if (fileType == 0xFF3BEF94L) {
			io = new ByteFileReader(file, "r");
		} else if (fileType == 0xF0985030L) {
			io = new ByteFileReader(cdfFile, "r");
		}
		BufferedImage dst = new BufferedImage(src.width, src.height, BufferedImage.TYPE_INT_RGB);
		if (src.indexType <= 0) {
			ByteBuffer buffer0 = ByteBuffer.allocate(dst.getWidth() * dst.getHeight() * 4);
			io.seek(src.dataStart);
			if (IS_DEBUG) {
				System.out.printf("BMP: data-%X\n", src.dataStart);
			}
			io.read(buffer0.array());
			IntBuffer buffer = buffer0.asIntBuffer();
			for (int i = 0; i < dst.getHeight(); i++) {
				for (int j = 0; j < dst.getWidth(); j++) {
					dst.setRGB(j, i, buffer.get());
				}
			}
			if (IS_DEBUG) {
				System.out.printf("%X\n", io.getFilePointer());
			}
		} else {
			IffBlockIndex index = src.index;
			int count = index.getIndexCount();
			int total = 0;
			for (int i = count - 1; i > 0; i--) {
				total += index.getSize(i);
			}
			ByteBuffer buffer0 = ByteBuffer.allocate(total);
			int writeCount = 0;
			for (int i = count - 1; i > 0; i--) {
				long addr = index.getAddr(i);
				long size = index.getSize(i);
				io.seek(addr);
				if (IS_DEBUG) {
					System.out.printf("BMP: data-%X\n", addr);
				}
				io.read(buffer0.array(), writeCount, (int) size);
				writeCount += size;
			}
			IntBuffer buffer = buffer0.asIntBuffer();
			for (int i = 0; i < dst.getHeight(); i++) {
				for (int j = 0; j < dst.getWidth(); j++) {
					dst.setRGB(j, i, buffer.get());
				}
			}
			if (IS_DEBUG) {
				System.out.printf("%X\n", io.getFilePointer());
			}
		}
		return dst;
	}

	/**
	 * Get DDS Texture data
	 * @param src
	 * @return
	 * @throws IOException
	 */
	private BufferedImage getDDSTexture(N2kTextureFile src) throws IOException {
		PluginDDS dds = new PluginDDS();
		DDSHEADER header = dds.createNewHeander();
		header.setDwWidth(src.getWidth());
		header.setDwHeight(src.getHeight());
		header.setPixelFormatStr(src.getPixelFormat());
		dds.setHeader(header);
		ByteFileReader io = null;
		if (fileType == 0xFF3BEF94L) {
			io = new ByteFileReader(file, "r");
		} else if (fileType == 0xF0985030L) {
			io = new ByteFileReader(cdfFile, "r");
		}
		BufferedImage dst = null;
		if (src.indexType <= 0) {
			io.seek(src.dataStart);
			dst = dds.convertToImage(io);
		} else {
			IffBlockIndex index = src.index;
			int count = index.getIndexCount();
			long totalSize = 0;
			for (int i = 0; i < count; i++) {
				totalSize += index.getSize(i);
			}
			ByteBuffer buffer0 = ByteBuffer.allocate((int) totalSize);
			int witeCount = 0;
			for (int i = count - 1; i >= 0; i--) {
				long addr = index.getAddr(i);
				long size = index.getSize(i);
				io.seek(addr);
				if (IS_DEBUG) {
					System.out.printf("DDS: data-%X-%X\n", addr, size);
				}
				io.read(buffer0.array(), witeCount, (int) size);
				witeCount += size;
			}
			buffer0.rewind();
			dst = dds.convertToImage(buffer0);
			if (IS_DEBUG) {
				System.out.printf("%X\n", io.getFilePointer());
			}
		}
		io.close();
		return dst;
	}

}
